﻿
using System;
using System.Text;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

using System.Reactive.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Threading.Tasks;

using alib.Debugging;
using alib.String;

using alib.Reflection;
using alib.Enumerable;
using alib.Concurrency;
using alib.Collections.ReadOnly;
using alib.Dictionary;
using alib.BitArray;
using alib.Memory;

namespace agree
{
	using transfer.old;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// ArrayTfs : low-impact, flat array edge storage
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{this.ShortDisplayInfo(),nq}")]
#if DEBUG
	[DebuggerTypeProxy(typeof(arraytfs_debug_display))]
#endif
	public unsafe partial class ArrayTfs : _atfs_base
	{
		static ArrayTfs()
		{
			var bf = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
			foreach (var mi in typeof(ArrayTfs).GetMethods(bf))
			{
				MethodInfo m = mi.IsGenericMethod ? mi.MakeGenericMethod(typeof(Grammar)) : mi;
				RuntimeHelpers.PrepareMethod(m.MethodHandle);
			}
		}


		///////////////////////////////////////////////////////
		/// 
		public static ArrayTfs FromTdl(IRtParent parent, Restrictor r, Tdl s_tdl)
		{
			var tdlt = new TdlTokenizer(parent, s_tdl);

			var tokg = Observable.Wait(new TdlConstraintGrouper(tdlt, tdlt)).constraints;

			var tfs_tdl = TfsBuilder.Tdl.Build(parent, r, tokg);

			if (tfs_tdl == null)
				throw new Exception("no effective constraints; all are vacuous.");
			Debug.Assert(tfs_tdl.ftm == r.FeatMgr);
			return new ArrayTfs(parent, tfs_tdl, Tfs.Flags.None);
		}
		///
		///////////////////////////////////////////////////////


		///////////////////////////////////////////////////////
		/// 
		static int _write_unifier_result(
			arr_tfs_entry* pate,
			Unification u,
			ushort* phf,
			TaggedRelMode tgrm)
		{
			Unification.Slot* ps_base = u.ps_base;
			Unification.Slot* ps_last = u.ps_last;
			uint cgen = u.cgen;
#if DEBUG
			arr_tfs_entry* _pate = pate;
#endif
			Unification.Slot* pz;
			int t;
			ushort* pus;
			int j = 0;

			for (pz = ps_base; pz < ps_last; pz++)
			{
				if (pz->gen == cgen)
				{
					pate->e_ul = ((t = pz->d_over) == 0 ? pz : ((Unification.Slot*)((long)ps_base - (t << SHL_PS))))->e_ul;
					*(ushort*)pate = pz->i_feat;

					pate->mark = (short)*(pus = (ushort*)((long)ps_base + (pz->ix_upper + 20)));
					pate->next = *(pus = phf + (byte)(*(ushort*)pate ^ *pus));
					pate->tgrm = tgrm;

					*pus = (ushort)++j;
					pate++;
				}
			}
#if DEBUG
			return (int)(pate - _pate);
#else
			return 0;
#endif
		}
		///
		///////////////////////////////////////////////////////


		///////////////////////////////////////////////////////
		/// 
		static ulong _write_unifier_result_varbits(arr_tfs_entry* _pate, Unification u, ushort* phf, TaggedRelMode tgrm)
		{
			arr_tfs_entry* pate = _pate;
			Unification.Slot* pz, ps_base = u.ps_base;
			int t;
			short m;
			ushort* pus;
			ushort j = 0;
			uint cgen = u.cgen;
			Unification.Slot* ps_last = u.ps_last;
			ulong varbits = 0;
			int ifeat_instloc = u.ftm.mrsm.ifeat_instloc;
			for (pz = ps_base; pz < ps_last; pz++)
			{
				if (pz->gen == cgen)
				{
					pate->e_ul = ((t = pz->d_over) == 0 ? pz : ((Unification.Slot*)((long)ps_base - (t << SHL_PS))))->e_ul;
					pate->i_feat = (short)(t = pz->i_feat);
					pate->mark = (m = (short)((Unification.Slot*)((long)ps_base + pz->ix_upper))->m_src);
					pate->tgrm = tgrm;
					pate->next = *(pus = phf + (byte)(t ^ m));
					*pus = ++j;

					if (t == ifeat_instloc && pate->e_FlagsId.IsSkolemValue())
						varbits |= (ulong)1 << ((int)(pate->e_FlagsId & Edge.Flag.subid_mask) - 1);

					pate++;
				}
			}
			Debug.Assert(pate - _pate == u.count_p3);
			return varbits;
		}
		///
		///////////////////////////////////////////////////////



		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected ArrayTfs(IRtParent parent, IIdentity prv, Restrictor r, Flags opts)
			: base(parent, prv, r, opts)
		{
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// clone an ArrayTfs (or derived instance)
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public ArrayTfs(IRtParent parent, Tfs to_clone)
			: base(parent, to_clone)
		{
			ArrayTfs htfs = to_clone as ArrayTfs;
			if (htfs != null)
			{
				fixed (ushort* _phf_dst = h.pfirst, _phf_src = htfs.h.pfirst)
				{
					long* phf_dst = (long*)_phf_dst, phf_src = (long*)_phf_src;
					int i = 64;
					while (0 < i--)
						*phf_dst++ = *phf_src++;
				}
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected ArrayTfs(IRtParent parent, Grammar g)
			: this(parent, g, g.ftm.r_none, Flags.Expanded)
		{
			this.entries = arr_tfs_entry.None;
			this.c_act = 0;
			this.c_corefs = 0;
			this._top_edge = default(Edge);
		}
		public static ArrayTfs get_top_tfs(IRtParent parent, Grammar g) { return new ArrayTfs(parent, g); }


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// construct an ArrayTfs from a Writeable TFS
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public ArrayTfs(IRtParent parent, TfsBuilder.Tdl tfs_tdl, Flags opts)
			: this(parent, tfs_tdl)
		{
			Debug.Assert(tfs_tdl._top_edge.Mark == 1);
			Debug.Assert(this.c_act == tfs_tdl.EdgeCount);

			this._top_edge = tfs_tdl._top_edge;
			int i;

			List<int> extras = null;
			if ((opts & Flags.RootCoverage) != 0)
			{
				this.flags |= Flags.RootCoverage;
				foreach (int fix in Type.fc.rg_fix)
				{
					int m;
					if (tfs_tdl.TryGetFlagsMark(fix, 1, out m) == 0 && m == 0)
						(extras = extras ?? new List<int>()).Add(fix);
				}
				if (extras != null)
					c_act += extras.Count;
			}

			this.entries = new arr_tfs_entry[c_act > 256 ? c_act : 256];

			fixed (ushort* phf = this.h.pfirst)
			fixed (arr_tfs_entry* _base = entries)
			{
				arr_tfs_entry* pate = _base;
				if (extras != null)
					foreach (int fix in extras)
					{
						pate->i_feat = (short)fix;
						pate->mark = 1;
						pate++;
					}
				tfs_tdl.CopyToArrayTfsEntries(pate);
				pate = _base;

				Debug.Assert(c_corefs == 0);
				int next_coref_mark = -1;
				for (i = 0; i < c_act; i++)
				{
					int m_old = pate->e_Mark;
					if (pate->e_FlagsId < 0 && m_old > 0)
					{
						short m_new = (short)next_coref_mark--;

						arr_tfs_entry* pate2 = _base;
						for (int j = 0; j < c_act; j++)
						{
							if (pate2->mark == m_old)
								pate2->mark = m_new;
							if (pate2->e_Mark == m_old)
								pate2->e_Mark = m_new;
							pate2++;
						}
					}
					pate++;
				}
				base.c_corefs = -next_coref_mark - 1;
				Debug.Assert(c_corefs >= 0);

				pate = _base;
				for (i = 0; i < c_act; )
				{
					i++;
					int v = (byte)(pate->i_feat ^ pate->mark);
					pate->next = phf[v];
					phf[v] = (ushort)i;
					pate++;
				}
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public ArrayTfs(IRtParent parent, IIdentity prv, Restrictor r, Tfs.Flags opts, Edge e, ICollection<arr_tfs_entry> rgent, int c_corefs)
			: this(parent, prv, r, opts)
		{
			this._top_edge = e;
			Debug.Assert(Type.HasAppropriateFeatures || (opts & Flags.MultiRoot) != 0);
			base.c_corefs = c_corefs;

			IList<int> extras = null;
			if ((opts & Flags.RootCoverage) != 0)
			{
				if (rgent == null)
					this.c_act = 0;

				if (this.c_act == 0)
					extras = Type.fc.rg_fix;
				else
				{
					extras = new List<int>(Type.fc.rg_fix);
					foreach (var ate in rgent)
						if (ate.mark == 1)
							extras.Remove(ate.i_feat);
				}
				if (extras.Count == 0)
					extras = null;
				else
					this.c_act += extras.Count;
			}
			else
				this.c_act = rgent.Count;

			Debug.Assert(c_act > 0);

			this.entries = new arr_tfs_entry[c_act > 256 ? c_act : 256];

			fixed (arr_tfs_entry* _pate = entries)
			fixed (ushort* phf = h.pfirst)
			{
				arr_tfs_entry* pate = _pate;
				ushort i = 0;
				ushort* pus;
				if (extras != null)
					foreach (int fix in extras)
					{
						pate->i_feat = (short)fix;
						pate->mark = 1;
						pus = phf + (byte)(fix ^ 1);

						pate->next = *pus;
						*pus = ++i;
						pate++;
					}
				if (rgent != null)
					foreach (var ate in rgent)
					{
						pus = phf + (byte)((pate->i_feat = ate.i_feat) ^ (pate->mark = ate.mark));
						pate->e = ate.e;
						pate->next = *pus;
						*pus = ++i;
						pate++;
					}
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public ArrayTfs(IRtParent parent, Unification u, Tfs.Flags opts, Edge e_top, int c_corefs)
			: base(parent, parent, u.r, opts)
		{
			this._top_edge = e_top;
			base.c_corefs = c_corefs;
			this.c_act = u.count_p3;
			this.entries = new arr_tfs_entry[c_act > 256 ? c_act : 256];

			var tgrm = new TaggedRelMode(Grammar.grm_idx, RelMode.None);

			fixed (arr_tfs_entry* _pate = entries)
			fixed (ushort* phf = h.pfirst)
			{
				if ((opts & Flags.WantVarbits) == 0)
				{
					int c = _write_unifier_result(_pate, u, phf, tgrm);
					Debug.Assert(c == c_act);
				}
				else
				{
					this.varbits = _write_unifier_result_varbits(_pate, u, phf, tgrm);
					flags |= Flags.Skolemized;
				}
			}
		}

#if false
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// burn skolem constants into a new copy of the given TFS
		/// </summary>
		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public ArrayTfs(IRtParent parent, Grammar g, ArrayTfs to_burn, SlotDictionary<int, Edge.Flag> st)
			: this(parent, to_burn)
		{
			if (st.Count != 0)
				new _burn_helper(g, st).go(out this.entries, to_burn);
			this.flags |= Flags.Skolemized;
		}

		public struct _burn_helper
		{
			public _burn_helper(Grammar g, SlotDictionary<int, Edge.Flag> st)
			{
				this.g = g;
				this.st = st;
			}
			readonly SlotDictionary<int, Edge.Flag> st;
			Grammar g;

			public void go(out arr_tfs_entry[] entries, Tfs to_burn)
			{
				entries = new arr_tfs_entry[to_burn.entries.Length];
				int ifeat_instloc = g.mrsm.ifeat_instloc;
				int i;
				for (i = 0; i < to_burn.c_act; i++)
					if ((entries[i] = to_burn.entries[i]).i_feat == ifeat_instloc)
						do_one(ref entries[i]);
			}

			void do_one(ref arr_tfs_entry ate)
			{
				Edge.Flag f;
				if (st.TryGetValue(ate.mark, out f))
				{
					if (ate.e_FlagsId < 0)
						f |= Edge.Flag.Coreference;
					ate.e_FlagsId = f;
				}
			}
		};
#endif

		public hash_tab h;

		public struct hash_tab
		{
			public fixed ushort pfirst[256];
		};

		public sealed override int EdgeCount { get { return c_act; } }
		public sealed override int SlotCount { get { return c_act; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public sealed override SlotRefArray SlotRefArray
		{
			get
			{
				int[] arr = new int[c_act + 1];
				for (int i = 0; i < arr.Length; i++)
					arr[i] = i;
				return new SlotRefArray(this, arr);
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public sealed override Edge.Flag TryGetFlagsMark(int i_feat, int mark, out int m)
		{
			Edge.Flag f;
			fixed (ushort* phf = h.pfirst)
			fixed (arr_tfs_entry* _pe = entries)
				f = TryGetFlagsMark(phf, _pe, i_feat, mark, out m);
			return f;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public static Edge.Flag TryGetFlagsMark(ushort* phf, arr_tfs_entry* _pe, int i_feat, int mark, out int m)
		{
#if DEBUG
			if (mark == 0)
				throw new Exception();
#endif
			m = 0;
			Edge.Flag f = 0;
			int ix;
			if ((ix = phf[(byte)(i_feat ^ mark)]) != 0)
			{
				*((short*)&i_feat + 1) = (short)mark;
				long pe;
				do
					if (*(int*)(pe = ((long)_pe + (uint)((ix - 1) << 4))) == i_feat)
					{
						m = *(int*)(pe + 12);
						f = *(Edge.Flag*)(pe + 8);
						break;
					}
				while ((ix = *(ushort*)(pe + 6)) != 0);
			}
			return f;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public sealed override int GetEdgeIndex(int i_feat, int mark, Edge* e)
		{
			Debug.Assert(mark != 0);
			int ix;
			fixed (ushort* phf = h.pfirst)
				ix = phf[(byte)(i_feat ^ mark)];
			if (ix != 0)
				fixed (arr_tfs_entry* _pe = entries)
				{
					*((short*)&i_feat + 1) = (short)mark;
					arr_tfs_entry* pe;
					do
						if (*(int*)(pe = (arr_tfs_entry*)((long)_pe + ((ix - 1) << 4))) == i_feat)
						{
							if (e != null)
								*(ulong*)e = *((ulong*)pe + 1);
							break;
						}
					while ((ix = pe->next) != 0);
				}
			return ix;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public sealed override bool TryGetEdge(int i_feat, int mark, out Edge e)
		{
			Debug.Assert(mark != 0);
			int ix;
			e = default(Edge);

			fixed (ushort* phf = h.pfirst)
				ix = phf[(byte)(i_feat ^ mark)];
			if (ix != 0)
				fixed (arr_tfs_entry* _pe = entries)
				{
					*((short*)&i_feat + 1) = (short)mark;
					arr_tfs_entry* pe;
					do
						if (*(int*)(pe = (arr_tfs_entry*)((long)_pe + ((ix - 1) << 4))) == i_feat)
						{
							e = pe->e;
							break;
						}
					while ((ix = pe->next) != 0);
				}
			return ix != 0;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[MethodImpl(MethodImplOptions.AggressiveInlining), DebuggerStepThrough]
		public override void GetIndexData(int ix1, arr_tfs_entry* pate)
		{
			if (ix1 == 0)
			{
				pate->FeatMark = FeatMark.RootNode;
				pate->e = _top_edge;
			}
			else
			{
				*pate = entries[ix1 - 1];
#if false
				
				if (g != null)
					grm_idx = g.grm_idx;
				pate->tgrm = new TaggedRelMode(grm_idx, RelMode.None);
#endif
			}
		}


#if REVERSE_INDEX
		public override Edge GetCorefEdge(int out_mark)
		{
			if (out_mark == _top_edge.Mark)
				return _top_edge;

			int[] ret;
			if (!_reverse_index.TryGetValue(out_mark, out ret))
				return default(Edge);

			return entries[ret[0] - 1].e;
		}
#endif

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		bool _root_feat_covered(arr_tfs_entry* pate, int i_feat)
		{
			int ix;
			fixed (ushort* phf = h.pfirst)
				ix = phf[(byte)(i_feat ^ 1)];
			if (ix != 0)
			{
				arr_tfs_entry* pe;
				do
					if ((pe = &pate[ix - 1])->i_feat == i_feat && pe->mark == 1)
						return true;
				while ((ix = pe->next) != 0);
			}
			return false;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool IsRootCovered
		{
			get
			{
				var arr = entries.Where(ate => ate.mark == 1).Select(ate => ate.i_feat).ToArray();
				if (!arr.IsDistinct())
					throw new Exception("duplicate root features in ArrayTfs");
				return arr.Length == Type.fc.rg_fix.Length;
			}
		}
	};
}
